home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / GLUT-3.7 / PROGS / ADVANCED / multilight.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  13.6 KB  |  537 lines

  1.  
  2. /* Copyright (c) Mark J. Kilgard, 1997.  */
  3.  
  4. /* This program is freely distributable without licensing fees  and is
  5.    provided without guarantee or warrantee expressed or  implied. This
  6.    program is -not- in the public domain. */
  7.  
  8. /* This program demonstrates virtualization of OpenGL's lights.  The idea is
  9.    that if an object is lit by many lights, it is computationally more
  10.    efficient to calculate the approximate lighting contribution of the
  11.    various lights per-object and only enable the "brightest" lights while
  12.    rendering the object.  This also lets you render scenes with more lights
  13.    than the OpenGL implementation light (usually 8).  Two approaches are
  14.    used:  The "distance-based" approach only enables the 8 closest lights
  15.    based purely on distance.  The "Lambertian-based" approach accounts for
  16.    diffuse lighting contributions and approximates the diffuse contribution. */
  17.  
  18. #include <math.h>
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <GL/glut.h>
  23.  
  24. /* Some <math.h> files do not define M_PI... */
  25. #ifndef M_PI
  26. #define M_PI 3.14159265358979323846
  27. #endif
  28.  
  29. #define MIN_VALUE(a,b) (((a)<(b))?(a):(b))
  30. #define MAX_VALUE(a,b) (((a)>(b))?(a):(b))
  31.  
  32. enum {
  33.   DL_LIGHT_SPHERE = 1,
  34.   DL_BIG_SPHERE = 2,
  35.   DL_ICO = 3
  36. };
  37.  
  38. enum {
  39.   M_SPHERE, M_ICO, M_LABELS, M_LINEAR, M_QUAD, M_REPORT_SIG,
  40.   M_LAMBERTIAN, M_DISTANCE, M_TIME
  41. };
  42.  
  43. typedef struct _LightInfo {
  44.   GLfloat xyz[4];
  45.   GLfloat *rgb;
  46.   int enable;
  47. } LightInfo;
  48.  
  49. typedef struct _LightBrightness {
  50.   int num;
  51.   GLfloat brightness;
  52. } LightBrightness;
  53.  
  54. static int animation = 1;
  55. static int labelLights = 1;
  56. static int reportLightSignificance = 0;
  57. static int brightnessModel = M_LAMBERTIAN;
  58. static int numActiveLights;
  59. static int timeFrames = 0;
  60. static int singleBuffer = 0;
  61. /* *INDENT-OFF* */
  62.  
  63. static GLfloat modelAmb[4] = {0.1, 0.1, 0.1, 1.0};
  64. static GLfloat matAmb[4] = {0.2, 0.2, 0.2, 1.0};
  65. static GLfloat matDiff[4] = {0.8, 0.8, 0.8, 1.0};
  66. static GLfloat matSpec[4] = {0.4, 0.4, 0.4, 1.0};
  67. static GLfloat matEmission[4] = {0.0, 0.0, 0.0, 1.0};
  68.  
  69. GLfloat red[] = {1.0, 0.0, 0.0, 1.0};
  70. GLfloat green[] = {0.0, 1.0, 0.0, 1.0};
  71. GLfloat blue[] = {0.0, 0.0, 1.0, 1.0};
  72. GLfloat yellow[] = {1.0, 1.0, 0.0, 1.0};
  73. GLfloat magenta[] = {1.0, 0.0, 1.0, 1.0};
  74. GLfloat white[] = {1.0, 1.0, 1.0, 1.0};
  75. GLfloat dim[] = {0.5, 0.5, 0.5, 1.0};
  76.  
  77. LightInfo linfo[] = {
  78.   { {-4.0, 0.0, -10.0, 1.0}, yellow},
  79.   { {4.0, 0.0, -10.0, 1.0}, green},
  80.   { {-4.0, 0.0, -6.0, 1.0}, red},
  81.   { {4.0, 0.0, -6.0, 1.0}, blue},
  82.   { {-4.0, 0.0, -2.0, 1.0}, green},
  83.   { {4.0, 0.0, -2.0, 1.0}, yellow},
  84.   { {-4.0, 0.0, 2.0, 1.0}, blue},
  85.   { {4.0, 0.0, 2.0, 1.0}, red},
  86.   { {-4.0, 0.0, 6.0, 1.0}, yellow},
  87.   { {4.0, 0.0, 6.0, 1.0}, green},
  88.   { {-4.0, 0.0, 10.0, 1.0}, red},
  89.   { {4.0, 0.0, 10.0, 1.0}, blue},
  90. };
  91.  
  92. int lightState[8] = {1, 1, 1, 1, 1, 1, 1, 1};
  93. /* *INDENT-ON* */
  94.  
  95. #define MAX_LIGHTS (sizeof(linfo)/sizeof(linfo[0]))
  96.  
  97. int moving = 0, begin;
  98. GLfloat angle = 0.0;
  99. int object = M_SPHERE;
  100. int attenuation = M_QUAD;
  101. GLfloat t = 0.0;
  102.  
  103. void
  104. initLight(int num)
  105. {
  106.   glLightf(GL_LIGHT0 + num, GL_CONSTANT_ATTENUATION, 0.0);
  107.   if (attenuation == M_LINEAR) {
  108.     glLightf(GL_LIGHT0 + num, GL_LINEAR_ATTENUATION, 0.4);
  109.     glLightf(GL_LIGHT0 + num, GL_QUADRATIC_ATTENUATION, 0.0);
  110.   } else {
  111.     glLightf(GL_LIGHT0 + num, GL_LINEAR_ATTENUATION, 0.0);
  112.     glLightf(GL_LIGHT0 + num, GL_QUADRATIC_ATTENUATION, 0.1);
  113.   }
  114.   glLightfv(GL_LIGHT0 + num, GL_SPECULAR, dim);
  115. }
  116.  
  117. /* Draw a sphere the same color as the light at the light position so it is
  118.    easy to tell where the positional light sources are. */
  119. void
  120. drawLight(LightInfo * info)
  121. {
  122.   glPushMatrix();
  123.   glTranslatef(info->xyz[0], info->xyz[1], info->xyz[2]);
  124.   glColor3fv(info->rgb);
  125.   glCallList(DL_LIGHT_SPHERE);
  126.   glPopMatrix();
  127. }
  128.  
  129. /* Place the light's OpenGL light number next to the light's sphere.  To
  130.    ensure a readable number with good contrast, a black version of the number 
  131.    is drawn shifted a pixel to the left and right of the actual white number. 
  132.  */
  133. void
  134. labelLight(LightInfo * info, int num)
  135. {
  136.   GLubyte nothin = 0;
  137.   void *font = GLUT_BITMAP_HELVETICA_18;
  138.   int width = glutBitmapWidth(font, '0' + num);
  139.  
  140.   glPushMatrix();
  141.   glColor3f(0.0, 0.0, 0.0);
  142.   glRasterPos3f(info->xyz[0], info->xyz[1], info->xyz[2]);
  143.   glBitmap(1, 1, 0, 0, 4, 5, ¬hin);
  144.   glutBitmapCharacter(font, '0' + num);
  145.  
  146.   glBitmap(1, 1, 0, 0, 2 - width, 0, ¬hin);
  147.   glutBitmapCharacter(font, '0' + num);
  148.  
  149.   if (lightState[num]) {
  150.     glColor3fv(white);
  151.   } else {
  152.     /* Draw disabled lights dimmer. */
  153.     glColor3fv(dim);
  154.   }
  155.   glRasterPos3f(info->xyz[0], info->xyz[1], info->xyz[2]);
  156.   glBitmap(1, 1, 0, 0, 5, 5, ¬hin);
  157.   glutBitmapCharacter(font, '0' + num);
  158.   glPopMatrix();
  159. }
  160.  
  161. /* Comparison routine used by qsort. */
  162. int
  163. lightBrightnessCompare(const void *a, const void *b)
  164. {
  165.   LightBrightness *ld1 = (LightBrightness *) a;
  166.   LightBrightness *ld2 = (LightBrightness *) b;
  167.   GLfloat diff;
  168.  
  169.   /* The brighter lights get sorted close to top of the list. */
  170.   diff = ld2->brightness - ld1->brightness;
  171.  
  172.   if (diff > 0)
  173.     return 1;
  174.   if (diff < 0)
  175.     return -1;
  176.   return 0;
  177. }
  178.  
  179. void
  180. display(void)
  181. {
  182.   int i;
  183.   GLfloat x, y, z;
  184.   LightBrightness ld[MAX_LIGHTS];
  185.   int start, end;
  186.  
  187.   if (timeFrames) {
  188.     start = glutGet(GLUT_ELAPSED_TIME);
  189.   }
  190.   x = cos(t * 12.3) * 2.0;
  191.   y = 0.0;
  192.   z = sin(t) * 7.0;
  193.  
  194.   for (i = 0; i < MAX_LIGHTS; i++) {
  195.     GLfloat dx, dy, dz;
  196.     GLfloat quadraticAttenuation;
  197.  
  198.     /* Calculate object to light position vector. */
  199.     dx = (linfo[i].xyz[0] - x);
  200.     dy = (linfo[i].xyz[1] - y);
  201.     dz = (linfo[i].xyz[2] - z);
  202.  
  203.     quadraticAttenuation = dx * dx + dy * dy + dz * dz;
  204.  
  205.     if (brightnessModel == M_LAMBERTIAN) {
  206.       /* Lambertian surface-based brightness determination. */
  207.       GLfloat ex, ey, ez;
  208.       GLfloat nx, ny, nz;
  209.       GLfloat distance;
  210.       GLfloat diffuseReflection;
  211.  
  212.       /* Determine eye point location (remember we can rotate by angle). */
  213.       ex = 16.0 * sin(angle * M_PI / 180.0);
  214.       ey = 1.0;
  215.       ez = 16.0 * -cos(angle * M_PI / 180.0);
  216.  
  217.       /* Calculated normalized object to eye position direction (nx,ny,nz). */
  218.       nx = (ex - x);
  219.       ny = (ey - y);
  220.       nz = (ez - z);
  221.       distance = sqrt(nx * nx + ny * ny + nz * nz);
  222.       nx = nx / distance;
  223.       ny = ny / distance;
  224.       nz = nz / distance;
  225.  
  226.       /* True distance needed, take square root. */
  227.       distance = sqrt(quadraticAttenuation);
  228.  
  229.       /* Calculate normalized object to light postition direction (dx,dy,dz). 
  230.        */
  231.       dx = dx / distance;
  232.       dy = dy / distance;
  233.       dz = dz / distance;
  234.  
  235.       /* Dot product of object->eye and object->light source directions.
  236.          OpenGL's lighting equations actually force the diffuse contribution
  237.          to be zero if the dot product is less than zero.  For our purposes,
  238.          that's too strict since we are approximating the entire object with
  239.          a single object-to-eye normal. */
  240.       diffuseReflection = nx * dx + ny * dy + nz * dz;
  241.       if (attenuation == M_QUAD) {
  242.         /* Attenuate based on square of distance. */
  243.         ld[i].brightness = diffuseReflection / quadraticAttenuation;
  244.       } else {
  245.         /* Attenuate based on linear distance. */
  246.         ld[i].brightness = diffuseReflection / distance;
  247.       }
  248.     } else {
  249.       /* Distance-based brightness determination. */
  250.  
  251.       /* In theory, we are really determining brightness based on just the
  252.          linear distance of the light source, but since we are just doing
  253.          comparisons, there is no reason to waste time doing a square root. */
  254.  
  255.       /* Negation makes sure closer distances are "bigger" than further
  256.          distances for sorting. */
  257.       ld[i].brightness = -quadraticAttenuation;
  258.     }
  259.     ld[i].num = i;
  260.   }
  261.  
  262.   /* Sort the lights so that the "brightest" are listed first.  We really
  263.      want to just determine the first numActiveLights so a full sort is
  264.      overkill. */
  265.   qsort(ld, MAX_LIGHTS, sizeof(ld[0]), lightBrightnessCompare);
  266.  
  267.   if (reportLightSignificance) {
  268.     printf("\n");
  269.     for (i = 0; i < MAX_LIGHTS; i++) {
  270.       printf("%d: dist = %g\n", ld[i].num, ld[i].brightness);
  271.     }
  272.   }
  273.   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  274.  
  275.   glPushMatrix();
  276.   glRotatef(angle, 0.0, 1.0, 0.0);
  277.  
  278.   glDisable(GL_LIGHTING);
  279.   for (i = 0; i < MAX_LIGHTS; i++) {
  280.     drawLight(&linfo[i]);
  281.   }
  282.  
  283.   /* After sorting, the first numActiveLights (ie, <8) light sources are the
  284.      light sources with the biggest contribution to the object's lighting.
  285.      Assign these "virtual lights of significance" to OpenGL's actual
  286.      available light sources. */
  287.  
  288.   glEnable(GL_LIGHTING);
  289.   for (i = 0; i < numActiveLights; i++) {
  290.     if (lightState[i]) {
  291.       int num = ld[i].num;
  292.  
  293.       glLightfv(GL_LIGHT0 + i, GL_POSITION, linfo[num].xyz);
  294.       glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, linfo[num].rgb);
  295.       glEnable(GL_LIGHT0 + i);
  296.     } else {
  297.       glDisable(GL_LIGHT0 + i);
  298.     }
  299.   }
  300.  
  301.   glPushMatrix();
  302.   glTranslatef(x, y, z);
  303.   switch (object) {
  304.   case M_SPHERE:
  305.     glCallList(DL_BIG_SPHERE);
  306.     break;
  307.   case M_ICO:
  308.     glCallList(DL_ICO);
  309.     break;
  310.   }
  311.   glPopMatrix();
  312.  
  313.   if (labelLights) {
  314.     glDisable(GL_DEPTH_TEST);
  315.     glDisable(GL_LIGHTING);
  316.     for (i = 0; i < numActiveLights; i++) {
  317.       labelLight(&linfo[ld[i].num], i);
  318.     }
  319.     glEnable(GL_DEPTH_TEST);
  320.   }
  321.   glPopMatrix();
  322.  
  323.   if (timeFrames) {
  324.     glFinish();
  325.     end = glutGet(GLUT_ELAPSED_TIME);
  326.     printf("Speed %.3g frames/sec (%d ms)\n",
  327.       1000.0 / (end - start), end - start);
  328.   }
  329.   if (!singleBuffer) {
  330.     glutSwapBuffers();
  331.   }
  332. }
  333.  
  334. void
  335. idle(void)
  336. {
  337.   t += 0.005;
  338.   glutPostRedisplay();
  339. }
  340.  
  341. /* When not visible, stop animating.  Restart when visible again. */
  342. static void
  343. visible(int vis)
  344. {
  345.   if (vis == GLUT_VISIBLE) {
  346.     if (animation)
  347.       glutIdleFunc(idle);
  348.   } else {
  349.     if (!animation)
  350.       glutIdleFunc(NULL);
  351.   }
  352. }
  353.  
  354. /* Press any key to redraw; good when motion stopped and performance
  355.    reporting on. */
  356. /* ARGSUSED */
  357. static void
  358. key(unsigned char c, int x, int y)
  359. {
  360.   int i;
  361.  
  362.   switch (c) {
  363.   case 27:
  364.     exit(0);            /* IRIS GLism, Escape quits. */
  365.     break;
  366.   case ' ':
  367.     animation = 1 - animation;
  368.     if (animation)
  369.       glutIdleFunc(idle);
  370.     else
  371.       glutIdleFunc(NULL);
  372.     break;
  373.   case '0':
  374.   case '1':
  375.   case '2':
  376.   case '3':
  377.   case '4':
  378.   case '5':
  379.   case '6':
  380.   case '7':
  381.     lightState[c - '0'] = 1 - lightState[c - '0'];
  382.     break;
  383.   case 13:
  384.     for (i = 0; i < numActiveLights; i++) {
  385.       lightState[i] = 1;
  386.     }
  387.     break;
  388.   }
  389.   glutPostRedisplay();
  390. }
  391.  
  392. /* ARGSUSED3 */
  393. void
  394. mouse(int button, int state, int x, int y)
  395. {
  396.   if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
  397.     moving = 1;
  398.     begin = x;
  399.   }
  400.   if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
  401.     moving = 0;
  402.   }
  403. }
  404.  
  405. /* ARGSUSED1 */
  406. void
  407. motion(int x, int y)
  408. {
  409.   if (moving) {
  410.     angle = angle + (x - begin);
  411.     begin = x;
  412.     glutPostRedisplay();
  413.   }
  414. }
  415.  
  416. void
  417. menu(int value)
  418. {
  419.   int i;
  420.  
  421.   switch (value) {
  422.   case M_SPHERE:
  423.     object = M_SPHERE;
  424.     break;
  425.   case M_ICO:
  426.     object = M_ICO;
  427.     break;
  428.   case M_LABELS:
  429.     labelLights = 1 - labelLights;
  430.     break;
  431.   case M_LINEAR:
  432.   case M_QUAD:
  433.     attenuation = value;
  434.     for (i = 0; i < numActiveLights; i++) {
  435.       initLight(i);
  436.     }
  437.     break;
  438.   case M_REPORT_SIG:
  439.     reportLightSignificance = 1 - reportLightSignificance;
  440.     break;
  441.   case M_LAMBERTIAN:
  442.     brightnessModel = M_LAMBERTIAN;
  443.     glutSetWindowTitle("multilight (Lambertian-based)");
  444.     break;
  445.   case M_DISTANCE:
  446.     brightnessModel = M_DISTANCE;
  447.     glutSetWindowTitle("multilight (Distance-based)");
  448.     break;
  449.   case M_TIME:
  450.     timeFrames = 1 - timeFrames;
  451.     break;
  452.   case 666:
  453.     exit(0);
  454.   }
  455.   glutPostRedisplay();
  456. }
  457.  
  458. int
  459. main(int argc, char **argv)
  460. {
  461.   int i;
  462.  
  463.   glutInitWindowSize(400, 200);
  464.   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
  465.   glutInit(&argc, argv);
  466.  
  467.   for (i = 1; i < argc; i++) {
  468.     if (!strcmp("-sb", argv[i])) {
  469.       glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE);
  470.       singleBuffer = 1;
  471.     }
  472.   }
  473.  
  474.   glutCreateWindow("multilight");
  475.  
  476.   glClearColor(0.0, 0.0, 0.0, 0.0);
  477.  
  478.   glMatrixMode(GL_PROJECTION);
  479.   gluPerspective(50.0, 2.0, 0.1, 100.0);
  480.   glMatrixMode(GL_MODELVIEW);
  481.   gluLookAt(
  482.     0.0, 1.0, -16.0,
  483.     0.0, 0.0, 0.0,
  484.     0.0, 1.0, 0.);
  485.  
  486.   numActiveLights = MIN_VALUE(MAX_LIGHTS, 8);
  487.   for (i = 0; i < numActiveLights; i++) {
  488.     initLight(i);
  489.   }
  490.  
  491.   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, modelAmb);
  492.   glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
  493.   glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
  494.   glEnable(GL_CULL_FACE);
  495.   glEnable(GL_DEPTH_TEST);
  496.  
  497.   glMaterialfv(GL_FRONT, GL_AMBIENT, matAmb);
  498.   glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiff);
  499.   glMaterialfv(GL_FRONT, GL_SPECULAR, matSpec);
  500.   glMaterialfv(GL_FRONT, GL_EMISSION, matEmission);
  501.   glMaterialf(GL_FRONT, GL_SHININESS, 10.0);
  502.  
  503.   glNewList(DL_LIGHT_SPHERE, GL_COMPILE);
  504.   glutSolidSphere(0.2, 4, 4);
  505.   glEndList();
  506.  
  507.   glNewList(DL_BIG_SPHERE, GL_COMPILE);
  508.   glutSolidSphere(1.5, 20, 20);
  509.   glEndList();
  510.  
  511.   glNewList(DL_ICO, GL_COMPILE);
  512.   glutSolidIcosahedron();
  513.   glEndList();
  514.  
  515.   glutDisplayFunc(display);
  516.   glutVisibilityFunc(visible);
  517.   glutKeyboardFunc(key);
  518.   glutMouseFunc(mouse);
  519.   glutMotionFunc(motion);
  520.  
  521.   glutCreateMenu(menu);
  522.   glutAddMenuEntry("Sphere", M_SPHERE);
  523.   glutAddMenuEntry("Icosahedron", M_ICO);
  524.   glutAddMenuEntry("Linear attenuation", M_LINEAR);
  525.   glutAddMenuEntry("Quadratic attenuation", M_QUAD);
  526.   glutAddMenuEntry("Toggle Light Number Labels", M_LABELS);
  527.   glutAddMenuEntry("Report Light Significance", M_REPORT_SIG);
  528.   glutAddMenuEntry("Lambertian-based Significance", M_LAMBERTIAN);
  529.   glutAddMenuEntry("Distance-based Significance", M_DISTANCE);
  530.   glutAddMenuEntry("Time Frames", M_TIME);
  531.   glutAddMenuEntry("Quit", 666);
  532.   glutAttachMenu(GLUT_RIGHT_BUTTON);
  533.  
  534.   glutMainLoop();
  535.   return 0;             /* ANSI C requires main to return int. */
  536. }
  537.